package cn.broadmax.boot.log;

import cn.broadmax.boot.base.model.LoginUser;
import cn.broadmax.boot.util.ServletUtils;
import cn.broadmax.boot.util.SpringContextUtils;
import jexx.json.JsonUtil;
import jexx.util.Assert;
import jexx.util.ReflectUtil;
import jexx.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;


/**
 * 系统切片日志
 *
 * @author jeff
 */
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class SysLogAspect {

    private final LogService logService;

    @Around(value = "@annotation(cn.broadmax.boot.log.SysLog)")
    public Object saveLog(ProceedingJoinPoint point) throws Throwable{
        long beginTime = System.currentTimeMillis();
        Object result = point.proceed();
        long time = System.currentTimeMillis() - beginTime;
        try {
            saveLog(point, time);
        }
        catch (Exception e){
            log.error("", e);
        }
        return result;
    }

    private void saveLog(ProceedingJoinPoint point, long time){
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = null;
        if(attributes != null){
            request = attributes.getRequest();
        }

        Object[] args = point.getArgs();
        MethodSignature methodSignature = (MethodSignature)point.getSignature();
        String[] argNames = methodSignature.getParameterNames();
        Method method = methodSignature.getMethod();
        SysLog log = method.getAnnotation(SysLog.class);

        LoginUser subject = SpringContextUtils.getSubject();
        if(log != null){
            LogRecord record = new LogRecord();
            record.setUserId(subject != null ? subject.getUserId() : null);
            record.setLogType(log.type());
            record.setLogContent(log.value());
            record.setMethod(method.getName());
            LogType logType = log.type();
            if(LogType.LOGIN != logType){
                Object[] parameters = getParameter(log, argNames, args);
                String parameter = JsonUtil.toJSONString(parameters);
                record.setRequestParam(parameter);
            }

            if(request != null){
                record.setIp(ServletUtils.getIpAddrByRequest(request));
                record.setRequestUrl(request.getRequestURL().toString());
                record.setRequestType(request.getMethod());
                String userAgent = request.getHeader("User-Agent");
                record.setUserAgent(request.getHeader(StringUtil.substring(userAgent, 0, 100)));
            }

            record.setCostTime(time);
            record.setLogTime(LocalDateTime.now());
            logService.saveLog(record);
        }
    }

    private Object[] getParameter(SysLog log, String[] argNames, Object[] args){
        String[] parameters = log.parameters();
        Object[] obj = new Object[parameters.length];
        if(parameters.length == 0){
            return obj;
        }

        Map<String, Object> argMap = new HashMap<>(4);
        for (int i = 0; i < argNames.length; i++){
            Object arg = args[i];
            if (arg instanceof ServletRequest || arg instanceof ServletResponse || arg instanceof MultipartFile) {
                continue;
            }
            argMap.putIfAbsent(argNames[i], arg);
        }

        for(int i = 0; i < parameters.length; i++){
            String parameter = parameters[i];
            String[] ps = parameter.split("\\.");
            Assert.isTrue(ps.length == 1 || ps.length == 2, "SysLog has wrong parameter!");
            String argName = ps[0];
            Object arg = argMap.get(argName);
            Assert.notNull(arg);
            if(ps.length == 1){
                obj[i] = arg;
            }
            else{
                String argPropertyName = ps[1];
                obj[i] = ReflectUtil.getFieldValue(arg, argPropertyName);
            }
        }
        return obj;
    }

    private String getOperateRequestArg(Object[] args){
        Object[] arguments  = new Object[args.length];
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            if (arg instanceof ServletRequest || arg instanceof ServletResponse || arg instanceof MultipartFile) {
                continue;
            }
            arguments[i] = arg;
        }
        return JsonUtil.toJSONString(arguments);
    }



}
